package com.hu.web.aspect;


import cn.hutool.core.util.NumberUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson.JSONObject;
import com.hu.common.enums.BaseResultCodeEnum;
import com.hu.common.exception.NewBizException;
import com.hu.common.util.comm.RedisUtil;
import com.hu.common.util.user.IPUtil;
import com.hu.common.util.user.UserProvider;
import com.hu.web.annotation.RepeatRequestIntercept;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Method;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * @author zyz
 */
@Aspect
@Slf4j
@RequiredArgsConstructor
public class RepeatRequestAspect {

    private static final String REPEAT_REQUEST_REDIS_KEY = "repeat_request_redis_key:";

    private final RedisUtil redisUtil;
    private final UserProvider userProvider;

    @Around("@annotation(com.hu.web.annotation.RepeatRequestIntercept)")
    public Object before(ProceedingJoinPoint joinPoint) throws Throwable {
        String userId = Optional.ofNullable(userProvider.get()).map(m -> m.getUserId()).orElse(null);
        Object[] args = joinPoint.getArgs();
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();

        String requestInfo = IPUtil.getIpAddr() + joinPoint.getTarget() + method.getName() + userId + JSONObject.toJSONString(args);

        String md5 = DigestUtil.md5Hex(requestInfo.getBytes());
        String lockKey = REPEAT_REQUEST_REDIS_KEY.concat(md5);
        Boolean lockFlag = false;
        RepeatRequestIntercept annotation = method.getAnnotation(RepeatRequestIntercept.class);
        // RepeatRequestIntercept annotation = getDeclaredAnnotation(joinPoint);
        try {
            if (NumberUtil.compare(annotation.timeout(), 0) == 1) {
                lockFlag = redisUtil.setNx(lockKey, md5, annotation.timeout(), TimeUnit.SECONDS);
            } else {
                lockFlag = redisUtil.setNx(lockKey, md5);
            }
            if (lockFlag) {
                return joinPoint.proceed();
            } else {
                throw new NewBizException(BaseResultCodeEnum.REPETITIVE_OPERATION.getMessage());
            }
        } finally {
            if (NumberUtil.compare(annotation.timeout(), 0) != 1) {
                if (lockFlag) {
                    redisUtil.delete(lockKey);
                }
            }
        }
    }

    /**
     * 获取方法中声明的注解
     *
     * @param joinPoint
     * @return
     * @throws NoSuchMethodException
     */
    public RepeatRequestIntercept getDeclaredAnnotation(JoinPoint joinPoint) throws NoSuchMethodException {
        // 获取方法名
        String methodName = joinPoint.getSignature().getName();
        // 反射获取目标类
        Class<?> targetClass = joinPoint.getTarget().getClass();
        // 拿到方法对应的参数类型
        Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
        // 根据类、方法、参数类型（重载）获取到方法的具体信息
        Method objMethod = targetClass.getMethod(methodName, parameterTypes);
        // 拿到方法定义的注解信息
        RepeatRequestIntercept intercept = objMethod.getDeclaredAnnotation(RepeatRequestIntercept.class);
        // 返回
        return intercept;
    }
}
